<?php

namespace App\Services;

use App\Ad\Docking\DockingBase;
use App\Caches\AdPlanCache;
use App\Datalog\AdFirstup;
use App\Models\AdMediaDocking;
use App\Models\AdMonitor;
use App\Models\AdPlan;
use App\Models\FirstupAd;
use App\Models\Firstups;
use App\Models\Log\LogAdMonitor;
use App\Models\Log\LogAdMonitorInfo;
use App\Structs\MatchMonitorStruct;

class AdMatchMonitorService extends ServiceBase
{
    public function getMatchList($stime, $etime, $unique_nums, $ip)
    {
        $eYm   = date('Y-m', $etime);
        $where = [
            ['create_time', '>=', $stime],
            ['create_time', '<', $etime],
        ];

        // $data = [];
        $data = new \Illuminate\Database\Eloquent\Collection;
        for ($i = date('Y-m', $stime); $i <= $eYm; $i = date('Y-m', strtotime($i . '+1month'))) {
            $Ym   = date('Ym', strtotime($i));
            $data = $data->merge(LogAdMonitor::suffix($Ym)->where($where)->where(function ($query) use ($unique_nums, $ip) {
                $query->where('ip', $ip);
                foreach ($unique_nums as $key => $val) {
                    $query->orWhereIn($key, array_values($val));
                }
            })->get());
            // $data = array_merge($data, LogAdMonitor::suffix($Ym)->where($where)->where(function ($query) use ($unique_nums, $ip) {
            //     $query->where('ip', $ip);
            //     foreach ($unique_nums as $key => $val) {
            //         $query->orWhereIn($key, array_values($val));
            //     }
            // })->get()->toArray());
        }
        // dd($data);
        // 时间倒序，从最近到最远开始匹配
        $data = $data->sortByDesc(function ($product, $key) {
            return $product['create_time'];
        });
        // usort($data->items(), function ($a, $b) {
        //     return $a['create_time'] < $b['create_time'];
        // });

        return $data;
    }

    public function checkMatchMonitor($stime, $etime, LogAdMonitor $log, Firstups $firstup, $match_type, $unique_nums = [])
    {
        // 时间是否有效
        if ($log['create_time'] <= $stime || $log['create_time'] >= $etime) {
            return;
        }

        $plan = AdPlanCache::getInstance()->remember($log['plan_id']);

        // 游戏id判断
        if (empty($plan) || $plan['game_id'] != $firstup->game_id) {
            return;
        }

        //channel判断
        if ($plan['channel'] != $firstup->channel) {
            return;
        }

        switch ($match_type) {
            // 基于设备码匹配
            case FirstupAd::MATCH_DEVICE:
                foreach ($unique_nums as $dkey => $darr) {
                    if (!empty($log[$dkey]) && isset($darr[$log[$dkey]])) {
                        return true;
                    }
                }
                break;
            // 基于ip+ua匹配
            case FirstupAd::MATCH_IP:
                $media = DockingBase::getClass($plan['media_docking_id']);
                if (!$media->needCheckUa()) {
                    return false;
                }
                if (empty($log['ip']) || $log['ip'] != $firstup->ip) {
                    return false;
                }
                // 如果存在媒体买量分包id，则判断此id跟点击监控的plan_id是否一致
                $ad_package_id = intval($firstup->ad_package_id);
                if ($ad_package_id > 1 && $log['plan_id'] != $ad_package_id) {
                    return false;
                }
                // 如果激活时的包id为计划id，则判断此id跟点击监控的plan_id是否一致
                $package_id = intval($firstup->package_id);
                if ($package_id > 100000 && $log['plan_id'] != $package_id) {
                    return false;
                }
                return true;
                break;
        }

        return false;
    }

    // 根据激活模型匹配出他的点击回调数据
    public function getLogMonitor($firstup, $match_hour = 48, $click_extend_hour = 5)
    {
        $struct = new MatchMonitorStruct;

        // 获取设备码匹配条件
        $device_nums = [];
        $keys = FirstupService::getInstance()->getDeviceKeysByDeviceType($firstup->device_type);
        foreach ($keys as $dkey) {
            if (!FirstupService::getInstance()->validDeviceNum($firstup[$dkey])) {
                continue;
            }
            $device_nums[$dkey] = $firstup[$dkey];
        }

        if (empty($firstup->ip) && empty($device_nums)) {
            return $struct;
        }

        $unique_nums = [];
        $docking_list  = AdMediaDocking::get();
        foreach ($docking_list as $val) {
            $docking = DockingBase::getClass($val->id);
            $media_device_nums = $docking->getMonitorDeviceNum($firstup->device_type, $device_nums);
            if (empty($media_device_nums)) {
                continue;
            }

            foreach ($media_device_nums as $key => $device) {
                if (!FirstupService::getInstance()->validDeviceNum($device)) {
                    continue;
                }
                $unique_nums[$key][strval($device)] = strval($device);
            }
        }

        // 有小部分情况获取到激活，才获取到点击数据，所以此处允许点击时间和激活时间有时间误差波动
        $stime = $firstup->create_time - 3600 * $match_hour;
        $etime = $firstup->create_time + 3600 * $click_extend_hour;
        $logs  = $this->getMatchList($stime, $etime, $unique_nums, $firstup->ip);

        foreach ([FirstupAd::MATCH_DEVICE, FirstupAd::MATCH_IP] as $match_type) {
            foreach ($logs as $log) {
                $ret = $this->checkMatchMonitor($stime, $etime, $log, $firstup, $match_type, $unique_nums);
                if ($ret) {
                    $struct->logAdMonitor = $log;
                    $struct->matchType = $match_type;
                    break;
                }
            }
            if ($struct->matchType > 0) {
                break;
            }
        }

        if ($struct->matchType > 0) {
            $ym = $struct->logAdMonitor->getSuffixByTableName();
            $struct->logAdMonitorInfo = LogAdMonitorInfo::suffix($ym)->where('monitor_id', $log->id)->first();
        }

        return $struct;
    }

    /**
     * 通过激活数据与点击回调进行配对，如果配对成功则返回点击回调模型
     * 注意：点击回调是媒体异步通知的，所以有概率出现用户激活上报完毕了，点击回调还没通知过来
     * @param Firstups $firstup 激活模型
     * @return boolean
     */
    public function matchMonitor($firstup)
    {
        $firstup_ad = AdFirstupService::getInstance()->getOrCreateByFirstupId($firstup->id);

        // 如果已经匹配过则无需再处理
        if ($firstup_ad->monitor_id > 0) {
            return;
        }

        if ($firstup_ad->match_num > FirstupAd::MAX_MATCH_NUM) {
            return false;
        }

        $firstup_ad->match_num++;
        $firstup_ad->match_time = time();

        $struct = $this->getLogMonitor($firstup);
        if ($struct->matchType <= 0) {
            $firstup_ad->save();
            return false;
        }

        $log  = $struct->logAdMonitor;
        $plan = AdPlanCache::getInstance()->remember($log->plan_id);

        // 存储点击监控数据，防止删除
        $monitor              = new AdMonitor;
        $monitor->create_time = time();
        $monitor->plan_id     = $log->plan_id;
        $monitor->data        = $struct->logAdMonitorInfo->data ?? '';
        $monitor->save();

        // 记录点击回调数据id
        $firstup_ad->plan_id          = $log->plan_id;
        $firstup_ad->monitor_id       = $monitor->id;
        $firstup_ad->match_type       = $struct->matchType;
        $firstup_ad->media_user_id    = $plan['media_user_id'];
        $firstup_ad->media_id         = $plan['media_id'];
        $firstup_ad->media_docking_id = $plan['media_docking_id'];

        $docking  = DockingBase::getClass($plan['media_docking_id']);
        $docking->supplementMonitorInfo($firstup_ad, $monitor);

        $firstup_ad->save();

        // 匹配成功进行回传
        AdPostbackService::getInstance()->postbackFirstUp($firstup_ad);

        AdFirstup::getInstance()->addLog($firstup_ad->id);

        return true;
    }

    /**
     * 如果激活匹配不到广告点击监控，则我们去查看此package_id是否为短链的plan_id，如果是则将其归因到短链
     * 注意：请先跑完 matchMonitor 的次数后，再进行短链匹配，防止出现短链抢量
     * @param Firstups $firstup 激活模型
     * @return boolean
     */
    public function matchShortUrl($firstup)
    {
        $package_id = $firstup->package_id;
        $plan = AdPlan::find($package_id);
        if (!$plan) {
            return;
        }

        if ($plan->media_id != AdPlan::MEDIA_ID_SHORTURL) {
            return;
        }

        $firstup_ad = AdFirstupService::getInstance()->getOrCreateByFirstupId($firstup->id);
        // 如果已经匹配过则无需再处理
        if ($firstup_ad->plan_id > 0) {
            return;
        }

        // 记录点击回调数据id
        $firstup_ad->plan_id          = $package_id;
        $firstup_ad->match_type       = FirstupAd::MATCH_SHORTURL;
        $firstup_ad->media_user_id    = $plan['media_user_id'];
        $firstup_ad->media_id         = $plan['media_id'];
        $firstup_ad->media_docking_id = $plan['media_docking_id'];

        $firstup_ad->save();

        // 匹配成功进行回传
        AdPostbackService::getInstance()->postbackFirstUp($firstup_ad);

        AdFirstup::getInstance()->addLog($firstup_ad->id);

        return true;
    }

    /**
     * 如果激活匹配不到广告点击监控，短链package_id，则去匹配 ad_package_id
     * 注意：ad_package_id 可以归到哪条计划，但是无法知道所属的广告详情
     * @param Firstups $firstup 激活模型
     * @return boolean
     */
    public function matchAdPackageId($firstup)
    {
        $package_id = intval($firstup->ad_package_id);
        if ($package_id < 1) {
            return;
        }

        $plan = AdPlan::find($package_id);
        if (!$plan) {
            return;
        }

        $firstup_ad = AdFirstupService::getInstance()->getOrCreateByFirstupId($firstup->id);
        // 如果已经匹配过则无需再处理
        if ($firstup_ad->plan_id > 0) {
            return;
        }

        // 记录点击回调数据id
        $firstup_ad->plan_id          = $package_id;
        $firstup_ad->match_type       = FirstupAd::MATCH_ADPACKAGEID;
        $firstup_ad->media_user_id    = $plan['media_user_id'];
        $firstup_ad->media_id         = $plan['media_id'];
        $firstup_ad->media_docking_id = $plan['media_docking_id'];

        $firstup_ad->save();

        // 匹配成功进行回传
        AdPostbackService::getInstance()->postbackFirstUp($firstup_ad);

        AdFirstup::getInstance()->addLog($firstup_ad->id);

        return true;
    }
}
